/*
 * Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
 *
 * This file is part of SeMoLearning.
 *
 * SeMoLearning is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SeMoLearning is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SeMoLearning.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SENSOR_H_
#define SENSOR_H_

#include "semol/core/SemolExp.h" // DLL export/import defines

#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <vector>
#include <list>
#include <utilite/UEvent.h>
#include <utilite/UStl.h>
#include <utilite/ULogger.h>

namespace semol {


class Sensor
{
public:
	/*
	enum Type{
		kTypeImage=0,
		kTypeAudioFreqSqrdMagn,
		kTypeTwist,
		kTypeJointState,
		kTypeEnd};
		*/
	enum Type{
		kTypeImage=0,
		kTypeAudio,
		kTypeAudioFreq,
		kTypeAudioFreqSqrdMagn,
		kTypeJointState,
		kTypeTwist,
		kTypeEnd};

	static cv::Mat fromAudio(const std::vector<std::vector<char> > & frame, int bytesPerSample)
	{
		cv::Mat data;
		if(frame.size() && frame[0].size())
		{
			switch(bytesPerSample)
			{
			case 1:
				data = cv::Mat(frame.size(), frame[0].size(), CV_8S);
				break;
			case 2:
				UASSERT(frame[0].size()%2 == 0);
				data = cv::Mat(frame.size(), frame[0].size()/2, CV_16S);
				break;
			case 4:
				UASSERT(frame[0].size()%4 == 0);
				data = cv::Mat(frame.size(), frame[0].size()/4, CV_32F);
				break;
			default:
				UFATAL("bytePerSample must be 1, 2 or 4");
				break;
			}
			for(unsigned int i=0; i<frame.size(); ++i)
			{
				UASSERT(frame[i].size() == frame[0].size());
				memcpy(data.row(i).data, frame[i].data(), frame[i].size());
			}
		}
		return data;
	}

	static cv::Mat fromAudio(const std::vector<std::vector<float> > & frame)
	{
		cv::Mat data;
		if(frame.size() && frame[0].size())
		{
			data = cv::Mat(frame.size(), frame[0].size(), CV_32F);
			for(unsigned int i=0; i<frame.size(); ++i)
			{
				UASSERT(frame[i].size() == frame[0].size());
				memcpy(data.row(i).data, frame[i].data(), frame[i].size()*sizeof(float));
			}
		}
		return data;
	}

public:
	// Raw data + sensorimotor post data constructor
	Sensor(int id, const std::vector<float> & descriptor, Type type, const cv::Mat & rawData = cv::Mat()) :
		_id(id),
		_descriptor(descriptor),
		_rawData(rawData),
		_type(type),
		_saved(false),
		_modified(false),
		_totalReferences(0)
	{}
	int id() const {return _id;}
	const std::vector<float> & descriptor() const {return _descriptor;}
	const cv::Mat & rawData() const {return _rawData;}
	Type type() const {return _type;}

	virtual ~Sensor() {};

	void addRef(int nodeId)
	{
		std::map<int, int>::iterator iter = _references.find(nodeId);
		if(iter != _references.end())
		{
			(*iter).second += 1;
		}
		else
		{
			_references.insert(std::pair<int, int>(nodeId, 1));
		}
		++_totalReferences;
	}

	int removeAllRef(int signatureId)
	{
		int removed = uTake(_references, signatureId, 0);
		_totalReferences -= removed;
		return removed;
	}

	int totalReferences() const {return _totalReferences;}
	const std::map<int, int> & getReferences() const {return _references;} // (signature id , occurrence in the signature)
	bool isSaved() const {return _saved;}
	bool isModified() const {return _modified;}
	void setSaved(bool saved) {_saved = saved;}
	void setModified(bool modified) {_modified = modified;}

private:
	int _id;
	std::vector<float> _descriptor;
	cv::Mat _rawData;
	Type _type;

	bool _saved; // If it's saved to db
	bool _modified;
	int _totalReferences;
	std::map<int, int> _references; // (node id , occurrence in the node)

};

class SensorRaw
{
public:
	SensorRaw(Sensor::Type type, const cv::Mat & data) :
		_type(type),
		_data(data)
	{}
	virtual ~SensorRaw() {}
	Sensor::Type type() const {return _type;}
	const cv::Mat & data() const {return _data;}
private:
	Sensor::Type _type;
	cv::Mat _data;
};

}

#endif /* SENSOR_H_ */
